/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#pragma once
#include <boost/thread.hpp>
using boost::mutex;
using boost::thread;
using boost::condition_variable;

/////////////////////////////////////////////////////////////////////////////

class Snapshot
{
public:
	Snapshot(int width, int height, HBITMAP image, INT64 timeTaken);
	~Snapshot();
	
	int width, height;
	HBITMAP image;
	INT64 timeTaken;
};

/////////////////////////////////////////////////////////////////////////////

struct SnapshotSpec
{
	TaskData *task;
	
	bool vertical;
	int thickness;
	int maxLength;
	
	bool operator!=(const SnapshotSpec &rhs) const;
	bool operator<(const SnapshotSpec &rhs) const;
};

/////////////////////////////////////////////////////////////////////////////

class SnapshotRequest
{
public:
	SnapshotRequest(SnapshotSpec spec);
	~SnapshotRequest();
	
	SnapshotSpec getSpec();
	UINT64 getSnapshotTime();
	
	Snapshot *lock();
	void unlock();
	
	void setSnapshot(Snapshot *snapshot);
	void incrRefCount();
	void decrRefCount();
	
	void addNotifyRequest(HWND window);
	void removeNotifyRequest(HWND window);
	
	bool updatePending;
	bool updateCancelled;
	
private:
	SnapshotSpec spec;
	set<HWND> updateNotifyList;
	
	mutex sync;
	int refCount;
	Snapshot *latestSnapshot;
};

/////////////////////////////////////////////////////////////////////////////

class SnapshotElementState
{
public:
	SnapshotElementState(HWND owner, SnapshotRequest *request);
	~SnapshotElementState();
	
	SnapshotRequest *request;
	UINT64 lastUpdate;
	HWND owner;
};

/////////////////////////////////////////////////////////////////////////////

struct SnapshotLayout
{
	int outputWidth, outputHeight;
	vector<HWND> windows;
	vector<RECT> windowRects;
	vector<RECT> targetRects;
};
	
/////////////////////////////////////////////////////////////////////////////

class SnapshotManager
{
public:
	SnapshotManager();
	~SnapshotManager();
	
	SnapshotRequest *requestSnapshot(SnapshotSpec spec, HWND requestSource);
	void cancelRequest(SnapshotRequest *request);
	void updateSnapshots();
	SnapshotLayout *getTaskSnapLayout(SnapshotRequest *request);
	
protected:
	struct PendingUpdate
	{
		SnapshotLayout *layout;
		SnapshotRequest *request;
	};
	
	// Functions run from the main thread
	void requestUpdate(SnapshotRequest *request);
	
	// Functions run from the helper thread
	static void snapshotLoop(SnapshotManager *snapshotManager);
	PendingUpdate *getNextRequest();
	Snapshot *snap(SnapshotLayout *layout);
	HRGN getPreviewRegion(HWND window, const XFORM *transform);
	
	// Shared data structures
	mutex sync;
	condition_variable requestsWaiting;
	thread *snapper;
	bool exiting;
	
	deque<PendingUpdate*> pending;
	map<SnapshotSpec, SnapshotRequest*> requests;
	
	typedef BOOL (*PrintWindowType)(HWND hwnd, HDC hdcBlt, UINT nFlags);
	PrintWindowType printWindowFunc;
};
extern SnapshotManager *snapshotManager;

/////////////////////////////////////////////////////////////////////////////

class SnapshotElement
	:public LayoutElement
{
public:
	SnapshotElement(string prefix);
	~SnapshotElement();
	
	ElementSize getLength(const LayoutCacheNode *node);
	void draw(HDC drawContext, const LayoutCacheNode *node);
	bool changed(const LayoutCacheNode *node);
	void update(LayoutCacheNode *node);
	void deleteElementData(LayoutCacheNode *node, void *elementData);
	
private:
	SnapshotSpec getSnapshotSpec(const LayoutCacheNode *node);
	SIZE getDimensions(const LayoutCacheNode *node);
	
	bool useAspect;
	float maxAspect;
	int maxLength;
};
